Package org.python.pydev.plugin.nature

Source Code of org.python.pydev.plugin.nature.PythonPathNature

/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
/*
* Created on Jun 2, 2005
*
* @author Fabio Zadrozny
*/
package org.python.pydev.plugin.nature;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.QualifiedName;
import org.python.pydev.core.ExtensionHelper;
import org.python.pydev.core.ICodeCompletionASTManager;
import org.python.pydev.core.IInterpreterInfo;
import org.python.pydev.core.IInterpreterManager;
import org.python.pydev.core.IModulesManager;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.IPythonPathNature;
import org.python.pydev.core.MisconfigurationException;
import org.python.pydev.core.PropertiesHelper;
import org.python.pydev.core.PythonNatureWithoutProjectException;
import org.python.pydev.core.docutils.StringSubstitution;
import org.python.pydev.core.docutils.StringUtils;
import org.python.pydev.core.log.Log;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.ui.filetypes.FileTypesPreferencesPage;

import com.aptana.shared_core.io.FileUtils;
import com.aptana.shared_core.string.FastStringBuffer;

/**
* @author Fabio Zadrozny
*/
public class PythonPathNature implements IPythonPathNature {

    private volatile IProject fProject;
    private volatile PythonNature fNature;

    /**
     * This is the property that has the python path - associated with the project.
     */
    private static QualifiedName projectSourcePathQualifiedName = null;

    static QualifiedName getProjectSourcePathQualifiedName() {
        if (projectSourcePathQualifiedName == null) {
            projectSourcePathQualifiedName = new QualifiedName(PydevPlugin.getPluginID(), "PROJECT_SOURCE_PATH");
        }
        return projectSourcePathQualifiedName;
    }

    /**
     * This is the property that has the external python path - associated with the project.
     */
    private static QualifiedName projectExternalSourcePathQualifiedName = null;

    static QualifiedName getProjectExternalSourcePathQualifiedName() {
        if (projectExternalSourcePathQualifiedName == null) {
            projectExternalSourcePathQualifiedName = new QualifiedName(PydevPlugin.getPluginID(),
                    "PROJECT_EXTERNAL_SOURCE_PATH");
        }
        return projectExternalSourcePathQualifiedName;
    }

    /**
     * This is the property that has the external python path - associated with the project.
     */
    private static QualifiedName projectVariableSubstitutionQualifiedName = null;

    static QualifiedName getProjectVariableSubstitutionQualifiedName() {
        if (projectVariableSubstitutionQualifiedName == null) {
            projectVariableSubstitutionQualifiedName = new QualifiedName(PydevPlugin.getPluginID(),
                    "PROJECT_VARIABLE_SUBSTITUTION");
        }
        return projectVariableSubstitutionQualifiedName;
    }

    public void setProject(IProject project, IPythonNature nature) {
        this.fProject = project;
        this.fNature = (PythonNature) nature;
    }

    public IPythonNature getNature() {
        return this.fNature;
    }

    private boolean waited = false;

    /**
     * Returns a list of paths with the complete pythonpath for this nature.
     *
     * This includes the pythonpath for the project, all the referenced projects and the
     * system.
     */
    public List<String> getCompleteProjectPythonPath(IInterpreterInfo interpreter, IInterpreterManager manager) {
        IModulesManager projectModulesManager = getProjectModulesManager();
        if (projectModulesManager == null) {
            if (!waited) {
                waited = true;
                for (int i = 0; i < 10 && projectModulesManager == null; i++) {
                    //We may get into a race condition, so, try to see if we can get it.
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        //OK
                    }
                    projectModulesManager = getProjectModulesManager();
                }
            }
        }
        if (projectModulesManager == null) {
            return null;
        }
        return projectModulesManager.getCompletePythonPath(interpreter, manager);
    }

    private IModulesManager getProjectModulesManager() {
        IPythonNature nature = fNature;
        if (nature == null) {
            return null;
        }

        ICodeCompletionASTManager astManager = nature.getAstManager();
        if (astManager == null) {
            // AST manager might not be yet available
            // Code completion job is scheduled to be run
            return null;
        }

        return astManager.getModulesManager();
    }

    private static volatile long doFullSynchAt = -1;
    private static final Map<String, Long> directMembersChecked = new HashMap<String, Long>();

    /**
     * @return the project pythonpath with complete paths in the filesystem.
     */
    public String getOnlyProjectPythonPathStr(boolean addExternal) throws CoreException {
        String source = null;
        String external = null;
        String contributed = null;
        IProject project = fProject;
        PythonNature nature = fNature;

        if (project == null || nature == null) {
            return "";
        }

        //Substitute with variables!
        StringSubstitution stringSubstitution = new StringSubstitution(nature);

        source = getProjectSourcePath(true);
        if (addExternal) {
            external = getProjectExternalSourcePath(true);
        }
        contributed = stringSubstitution.performPythonpathStringSubstitution(getContributedSourcePath(project));

        if (source == null) {
            source = "";
        }
        //we have to work on this one to resolve to full files, as what is stored is the position
        //relative to the project location
        List<String> strings = StringUtils.splitAndRemoveEmptyTrimmed(source, '|');
        FastStringBuffer buf = new FastStringBuffer();

        IWorkspaceRoot root = null;

        ResourcesPlugin resourcesPlugin = ResourcesPlugin.getPlugin();
        for (String currentPath : strings) {
            if (currentPath.trim().length() > 0) {
                IPath p = new Path(currentPath);

                if (resourcesPlugin == null) {
                    //in tests
                    buf.append(currentPath);
                    buf.append("|");
                    continue;
                }

                if (root == null) {
                    root = ResourcesPlugin.getWorkspace().getRoot();
                }

                if (p.segmentCount() < 1) {
                    Log.log("Found no segment in: " + currentPath + " for: " + project);
                    continue; //No segment? Really weird!
                }

                //try to get relative to the workspace
                IContainer container = null;
                IResource r = null;
                try {
                    r = root.findMember(p);
                } catch (Exception e) {
                    Log.log(e);
                }

                if (!(r instanceof IContainer) && !(r instanceof IFile)) {

                    //If we didn't find the file, let's try to sync things, as this can happen if the workspace
                    //is still not properly synchronized.
                    String firstSegment = p.segment(0);
                    IResource firstSegmentResource = root.findMember(firstSegment);
                    if (!(firstSegmentResource instanceof IContainer) && !(firstSegmentResource instanceof IFile)) {
                        //we cannot even get the 1st part... let's do sync
                        long currentTimeMillis = System.currentTimeMillis();
                        if (doFullSynchAt == -1 || currentTimeMillis > doFullSynchAt) {
                            doFullSynchAt = currentTimeMillis + (60 * 2 * 1000); //do a full synch at most once every 2 minutes
                            try {
                                root.refreshLocal(p.segmentCount() + 1, null);
                            } catch (CoreException e) {
                                //ignore
                            }
                        }

                    } else {
                        Long doSynchAt = directMembersChecked.get(firstSegment);
                        long currentTimeMillis = System.currentTimeMillis();
                        if (doSynchAt == null || currentTimeMillis > doFullSynchAt) {
                            directMembersChecked.put(firstSegment, currentTimeMillis + (60 * 2 * 1000));
                            //OK, we can get to the 1st segment, so, let's do a refresh just from that point on, not in the whole workspace...
                            try {
                                firstSegmentResource.refreshLocal(p.segmentCount(), null);
                            } catch (CoreException e) {
                                //ignore
                            }
                        }

                    }

                    //Now, try to get it knowing that it's properly synched (it may still not be there, but at least we tried it)
                    try {
                        r = root.findMember(p);
                    } catch (Exception e) {
                        Log.log(e);
                    }
                }

                if (r instanceof IContainer) {
                    container = (IContainer) r;
                    buf.append(FileUtils.getFileAbsolutePath(container.getLocation().toFile()));
                    buf.append("|");

                } else if (r instanceof IFile) { //zip/jar/egg file
                    String extension = r.getFileExtension();
                    if (extension == null || FileTypesPreferencesPage.isValidZipFile("." + extension) == false) {
                        Log.log("Error: the path " + currentPath + " is a file but is not a recognized zip file.");

                    } else {
                        buf.append(FileUtils.getFileAbsolutePath(r.getLocation().toFile()));
                        buf.append("|");
                    }

                } else {
                    //We're now always making sure that it's all synchronized, so, if we got here, it really doesn't exist (let's warn about it)

                    //Not in workspace?... maybe it was removed, so, let the user know about it (and still add it to the pythonpath as is)
                    Log.log(IStatus.WARNING, "Unable to find the path " + currentPath + " in the project were it's \n"
                            + "added as a source folder for pydev (project: " + project.getName() + ") member:" + r,
                            null);

                    //No good: try to get it relative to the project
                    String curr = currentPath;
                    IPath path = new Path(curr.trim());
                    if (project.getFullPath().isPrefixOf(path)) {
                        path = path.removeFirstSegments(1);
                        if (FileTypesPreferencesPage.isValidZipFile(curr)) {
                            r = project.getFile(path);

                        } else {
                            //get it relative to the project
                            r = project.getFolder(path);
                        }
                        if (r != null) {
                            buf.append(FileUtils.getFileAbsolutePath(r.getLocation().toFile()));
                            buf.append("|");
                            continue; //Don't go on to append it relative to the workspace root.
                        }
                    }

                    //Nothing worked: force it to be relative to the workspace.
                    IPath rootLocation = root.getRawLocation();

                    //Note that this'll be cached for later use.
                    buf.append(FileUtils.getFileAbsolutePath(rootLocation.append(currentPath.trim()).toFile()));
                    buf.append("|");
                }
            }
        }

        if (external == null) {
            external = "";
        }
        return buf.append("|").append(external).append("|").append(contributed).toString();
    }

    /**
     * Gets the source path contributed by plugins.
     *
     * See: http://sourceforge.net/tracker/index.php?func=detail&aid=1988084&group_id=85796&atid=577329
     *
     * @throws CoreException
     */
    @SuppressWarnings("unchecked")
    private String getContributedSourcePath(IProject project) throws CoreException {
        FastStringBuffer buff = new FastStringBuffer();
        List<IPythonPathContributor> contributors = ExtensionHelper
                .getParticipants("org.python.pydev.pydev_pythonpath_contrib");
        for (IPythonPathContributor contributor : contributors) {
            String additionalPythonPath = contributor.getAdditionalPythonPath(project);
            if (additionalPythonPath != null && additionalPythonPath.trim().length() > 0) {
                if (buff.length() > 0) {
                    buff.append("|");
                }
                buff.append(additionalPythonPath.trim());
            }
        }
        return buff.toString();
    }

    public void setProjectSourcePath(String newSourcePath) throws CoreException {
        PythonNature nature = fNature;

        if (nature != null) {
            nature.getStore().setPathProperty(PythonPathNature.getProjectSourcePathQualifiedName(), newSourcePath);
        }
    }

    public void setProjectExternalSourcePath(String newExternalSourcePath) throws CoreException {
        PythonNature nature = fNature;
        if (nature != null) {
            nature.getStore().setPathProperty(PythonPathNature.getProjectExternalSourcePathQualifiedName(),
                    newExternalSourcePath);
        }
    }

    public void setVariableSubstitution(Map<String, String> variableSubstitution) throws CoreException {
        PythonNature nature = fNature;
        if (nature != null) {
            nature.getStore().setMapProperty(PythonPathNature.getProjectVariableSubstitutionQualifiedName(),
                    variableSubstitution);
        }
    }

    public void clearCaches() {
        doFullSynchAt = -1;
        directMembersChecked.clear();
    }

    public Set<String> getProjectSourcePathSet(boolean replace) throws CoreException {
        String projectSourcePath;
        PythonNature nature = fNature;
        if (nature == null) {
            return new HashSet<String>();
        }
        projectSourcePath = getProjectSourcePath(replace);
        return new HashSet<String>(StringUtils.splitAndRemoveEmptyTrimmed(projectSourcePath, '|'));
    }

    public String getProjectSourcePath(boolean replace) throws CoreException {
        String projectSourcePath;
        boolean restore = false;
        IProject project = fProject;
        PythonNature nature = fNature;

        if (project == null || nature == null) {
            return "";
        }
        projectSourcePath = nature.getStore().getPathProperty(PythonPathNature.getProjectSourcePathQualifiedName());
        if (projectSourcePath == null) {
            //has not been set
            return "";
        }

        //we have to validate it, because as we store the values relative to the workspace, and not to the
        //project, the path may become invalid (in which case we have to make it compatible again).
        StringBuffer buffer = new StringBuffer();
        List<String> paths = StringUtils.splitAndRemoveEmptyTrimmed(projectSourcePath, '|');
        IPath projectPath = project.getFullPath();
        for (String path : paths) {
            if (path.trim().length() > 0) {
                if (path.indexOf("${") != -1) { //Account for the string substitution.
                    buffer.append(path);
                } else {
                    IPath p = new Path(path);
                    if (p.isEmpty()) {
                        continue; //go to the next...
                    }
                    if (projectPath != null && !projectPath.isPrefixOf(p)) {
                        p = p.removeFirstSegments(1);
                        p = projectPath.append(p);
                        restore = true;
                    }
                    buffer.append(p.toString());
                }
                buffer.append("|");
            }
        }

        //it was wrong and has just been fixed
        if (restore) {
            projectSourcePath = buffer.toString();
            setProjectSourcePath(projectSourcePath);
            if (nature != null) {
                //yeap, everything has to be done from scratch, as all the filesystem paths have just
                //been turned to dust!
                nature.rebuildPath();
            }
        }
        return trimAndReplaceVariablesIfNeeded(replace, projectSourcePath, nature);
    }

    /**
     * Replaces the variables if needed.
     */
    private String trimAndReplaceVariablesIfNeeded(boolean replace, String projectSourcePath, PythonNature nature)
            throws CoreException {
        String ret = StringUtils.leftAndRightTrim(projectSourcePath, '|');
        if (replace) {
            StringSubstitution substitution = new StringSubstitution(nature);
            ret = substitution.performPythonpathStringSubstitution(ret);
        }
        return ret;
    }

    public String getProjectExternalSourcePath(boolean replace) throws CoreException {
        String extPath;

        PythonNature nature = fNature;
        if (nature == null) {
            return "";
        }
        //no need to validate because those are always 'file-system' related
        extPath = nature.getStore().getPathProperty(PythonPathNature.getProjectExternalSourcePathQualifiedName());

        if (extPath == null) {
            extPath = "";
        }
        return trimAndReplaceVariablesIfNeeded(replace, extPath, nature);
    }

    public List<String> getProjectExternalSourcePathAsList(boolean replaceVariables) throws CoreException {
        String projectExternalSourcePath = getProjectExternalSourcePath(replaceVariables);
        List<String> externalPaths = StringUtils.splitAndRemoveEmptyTrimmed(projectExternalSourcePath, '|');
        return externalPaths;
    }

    public Map<String, String> getVariableSubstitution() throws CoreException, MisconfigurationException,
            PythonNatureWithoutProjectException {
        return getVariableSubstitution(true);
    }

    /**
     * Returns the variables in the python nature and in the interpreter.
     */
    public Map<String, String> getVariableSubstitution(boolean addInterpreterInfoSubstitutions) throws CoreException,
            MisconfigurationException, PythonNatureWithoutProjectException {
        PythonNature nature = this.fNature;
        if (nature == null) {
            return new HashMap<String, String>();
        }

        Map<String, String> variableSubstitution;
        if (addInterpreterInfoSubstitutions) {

            IInterpreterInfo info = nature.getProjectInterpreter();
            Properties stringSubstitutionVariables = info.getStringSubstitutionVariables();
            if (stringSubstitutionVariables == null) {
                variableSubstitution = new HashMap<String, String>();
            } else {
                variableSubstitution = PropertiesHelper.createMapFromProperties(stringSubstitutionVariables);
            }
        } else {
            variableSubstitution = new HashMap<String, String>();
        }

        //no need to validate because those are always 'file-system' related
        Map<String, String> variableSubstitution2 = nature.getStore().getMapProperty(
                PythonPathNature.getProjectVariableSubstitutionQualifiedName());
        if (variableSubstitution2 != null) {
            if (variableSubstitution != null) {
                variableSubstitution.putAll(variableSubstitution2);
            } else {
                variableSubstitution = variableSubstitution2;
            }
        }

        //never return null!
        if (variableSubstitution == null) {
            variableSubstitution = new HashMap<String, String>();
        }
        return variableSubstitution;
    }

}
TOP

Related Classes of org.python.pydev.plugin.nature.PythonPathNature

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.